#include <SDL.h>
#include <cmath>
#include "model.h"

Model::~Model() { 
  // These deletions eliminate "still reachable"s in Valgrind  
  deleteBricks();
  deleteExplosions();
  delete ball;
  delete player;
  delete mfactory;
  delete factory;
  delete sound;
  delete strategy;
  delete io;
  delete gamedata;
}

void Model::makeBricks() {
	for(int j = 1;j<9;j++)
	{
		bricks.push_back(new Sprite(Vector2f(80*j,0),Vector2f(0,0),factory->getFrame("redbrick","brick")));
		bricks.push_back(new Sprite(Vector2f(80*j,40),Vector2f(0,0),factory->getFrame("bluebrick","brick")));
		bricks.push_back(new Sprite(Vector2f(80*j,80),Vector2f(0,0),factory->getFrame("greenbrick","brick")));
		//bricks.push_back(new MultiSprite(Vector2f(80*j,120),Vector2f(0,0),mfactory->getFrame("gbrick","gbrick"),"gbrick"));
		//bricks.push_back(new MultiSprite(Vector2f(80*j,160),Vector2f(0,0),mfactory->getFrame("sbrick","sbrick"),"sbrick"));
	}
}

void Model::deleteBricks(){
	std::list<Sprite*>::iterator it = bricks.begin();
	while(it != bricks.end()){
		delete *it;
		it++;
	}
	bricks.clear();
}
void Model::resetBricks(){
	deleteBricks();
	makeBricks();	
}
Model::Model() :

  gamedata(Gamedata::getInstance()),
  io(IOManager::getInstance()),
  strategy(NULL),
  sound(SDLSound::getInstance()),
  clock(),
  screen( io->getScreen() ),
  factory(new FrameFactory()),
  mfactory(new MultiFrameFactory()),
  world(factory->getFrame("nebula","nebula")),
  player(new Player(Vector2f(gamedata->getXmlInt("barPosX"),gamedata->getXmlInt("barPosY")),
			Vector2f(0,0),mfactory->getFrame("bar","bar"),"bar")),
  ball(new Ball(Vector2f(gamedata->getXmlInt("playerballPosX"),gamedata->getXmlInt("playerballPosY")),
			Vector2f(gamedata->getXmlInt("playerballVelX"),gamedata->getXmlInt("playerballVelY")),
			factory->getFrame("playerball","playerball"))),
  bricks(),
  explosions(),
  showHelp(false),
  playerCollided(false),
  bricksCollided(false)
  {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
      throw string("Unable to initializade SDL: ");
    }
    SDL_putenv(const_cast<char*>("SDL_VIDEO_CENTERED=center"));
    //strategy
     if ( gamedata->getXmlStr("CollisionStrategy") ==
       "RectangularCollisionStrategy") {
		strategy = new RectangularCollisionStrategy;
	 } else if ( gamedata->getXmlStr("CollisionStrategy") ==
        "MidPointCollisionStrategy") {
        strategy = new MidPointCollisionStrategy;
	 } else 
		strategy = new PerPixelCollisionStrategy;
    //sound
    sound->addChannel(gamedata->getXmlStr("hitFile"));
    sound->addChannel(gamedata->getXmlStr("explosionFile"));
    makeBricks();
    atexit(SDL_Quit);
    SDL_WM_SetCaption( gamedata->getXmlStr("appName").c_str(), NULL );
}

void Model::draw() const {
  
  world.draw();
  drawBricks();
  drawExplosions();
  player->draw();
  ball->draw();
  
  
 if(showHelp) {
	drawHelp();
 }
  	drawFps();	
  	drawCount();
  SDL_Flip(screen);
}

void Model::update() {
  unsigned ticks = clock.getElapsedTicks();
  player->update(ticks);
  ball->update(ticks);
  updateBricks(ticks);
  updateExplosions(ticks);
  playerCollision();
  bricksCollision();
  if(ball->Y() > gamedata->getXmlInt("worldHeight"))
  {
	  ball->setPosition(
	  Vector2f(gamedata->getXmlInt("playerballPosX"),gamedata->getXmlInt("playerballPosY")));
	  ball->setVelocity(ball->getInitVelocity());
	  resetBricks();
	  toggleHelp();
  }
  clock++;
}

void Model::pause(){
	//std::cout<<"call pause"<<std::endl;
	if(!clock.isPaused()){
		clock.pause();
	} else {
		clock.unpause();
	}
	//std::cout<<clock.isPaused()<<std::endl;
}

bool Model::isPaused() const {
	return clock.isPaused();
}

void Model::drawHelp() const {
	SDL_Color color = {0,255,0,0};
	io->printMessageAt("Help memu", 200, 110,color);
	io->printMessageAt("1.Press <p> or <f1> to pause or resume the game", 200, 140,color);
	io->printMessageAt("2.Press <LEFT> or <RIGHT> to move the bar", 200, 170,color);
	io->printMessageAt("3.Press <m> to pause or resume the bgm", 200, 200,color);
	io->printMessageAt("4.Press <e> to explode the bricks", 200, 230,color);
	io->printMessageAt("5.Press <r> to reset the bricks", 200, 260,color);
	io->printMessageAt("6.Press <q> or <Esc> to quit", 200, 290,color);
}

void Model::drawFps() const {
	SDL_Color color = {255,255,0,0};
	//clock.debug();
	io->printMessageValueAt("Fps: ",clock.getFps(),gamedata->getXmlInt("worldWidth")*3/4, 10,color);

}

void Model::drawCount() const {
	SDL_Color color = {255,255,0,0};
	//clock.debug();
	io->printMessageValueAt("Bricks: ",bricks.size(),gamedata->getXmlInt("worldWidth")*3/4, 30,color);
}

void Model::drawBricks() const {
	std::list<Sprite*>::const_iterator it = bricks.begin();
	while(it != bricks.end()){
		(*it)->draw();
		it++;
	}
}

void Model::updateBricks(unsigned ticks){
		std::list<Sprite*>::iterator it = bricks.begin();
	while(it != bricks.end()){
		(*it)->update(ticks);
		it++;
	}
}

void Model::playSound(int index){
	sound->operator [](index-1);
}

void Model::toggleBgm(){
	sound->toggleMusic();
}

void Model::explode(){	
	if(bricks.size() > 0) {
		std::list<Sprite*>::iterator it = bricks.begin();
		int n = gamedata->getXmlInt("brickChunks");
		explosions.push_back(new ExplodingSprite(**it, n));
		playSound(2);
		//cout<<bricks.size()<<endl;
		delete *it;
		bricks.erase(it);
	}
}

void Model::drawExplosions() const {
	std::list<ExplodingSprite*>::const_iterator it = explosions.begin();
	while(it != explosions.end()){
		(*it)->draw();
		it++;
	}
}

void Model::updateExplosions(unsigned ticks){
		std::list<ExplodingSprite*>::iterator it = explosions.begin();
	    while(it != explosions.end()){
		(*it)->update(ticks);
		if((*it)->chunkCount() == 0){
			delete *it;
			it = explosions.erase(it);
		} else it++;
	}
}

void Model::deleteExplosions(){
	std::list<ExplodingSprite*>::iterator it = explosions.begin();
	while(it != explosions.end()){
		delete *it;
		it++;
	}
	explosions.clear();
}

void Model::playerCollision(){
	if ( !strategy->execute(*player, *ball) ){
		playerCollided = false;
		return;
	}
		cout<<"Collision"<<endl;
	if(!playerCollided){
		playSound(1);
		playerCollided = true;
		float pleft = player->X();
	    float bleft = ball->X();
	    float pright = pleft+player->getFrame()->getWidth();
	    float bright = bleft+ball->getFrame()->getWidth();
	    if(bleft >= pleft && bright <= pright){
		  ball->velocityY(-ball->velocityY());
	    } else { 
		  ball->velocityY(-ball->velocityY());
		  ball->velocityX(-ball->velocityX());
	    }
  }
}

void Model::bricksCollision(){
	std::list<Sprite*>::iterator it = bricks.begin();
	float bleft;
	float brleft;
	float bright;
	float brright;
	while(it != bricks.end()){
		if(strategy->execute(*ball, **it))
		{
			bleft = ball->X();
			brleft = (*it)->X();
			bright = bleft+ball->getFrame()->getWidth();
			brright = brleft+(*it)->getFrame()->getWidth();
			if(bleft >= brleft && bright <= brright){
				ball->velocityY(-ball->velocityY());
			} else { 
				ball->velocityY(-ball->velocityY());
				ball->velocityX(-ball->velocityX());
			}
			int n = gamedata->getXmlInt("brickChunks");
			explosions.push_back(new ExplodingSprite(**it, n));
			playSound(2);
			delete *it;
			bricks.erase(it);
			return;
		}
		it++;
	}
}
